package com.hzncc.flowable_diboot.trial_contracts.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.diboot.core.binding.Binder;
import com.diboot.core.binding.QueryBuilder;
import com.diboot.core.service.impl.BaseServiceImpl;
import com.diboot.core.util.S;
import com.diboot.core.vo.JsonResult;
import com.diboot.core.vo.Pagination;
import com.diboot.iam.util.IamSecurityUtils;
import com.hzncc.flowable_diboot.enums.ContractAuditStatusEnum;
import com.hzncc.flowable_diboot.enums.ContractAuditTypeEnum;
import com.hzncc.flowable_diboot.service.impl.CommonService;
import com.hzncc.flowable_diboot.trial_contracts.dto.TrialContractAuditDTO;
import com.hzncc.flowable_diboot.trial_contracts.entity.TrialContract;
import com.hzncc.flowable_diboot.trial_contracts.entity.TrialContractAudit;
import com.hzncc.flowable_diboot.trial_contracts.mapper.TrialContractAuditMapper;
import com.hzncc.flowable_diboot.trial_contracts.mapper.TrialContractMapper;
import com.hzncc.flowable_diboot.trial_contracts.service.*;
import com.hzncc.flowable_diboot.trial_contracts.vo.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;


/**
 * 试用合同 相关Service实现类
 *
 * @author MyName
 * @version 1.0
 * @date 2024-10-15
 * Copyright © MyCorp
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class TrialContractServiceImpl extends BaseServiceImpl<TrialContractMapper, TrialContract> implements TrialContractService {

    private final TrialContractAuditService trialContractAuditService;
    private final TrialContractUpdateService trialContractUpdateService;
    private final TrialContractCancelService trialContractCancelService;
    private final TrialContractItemService trialContractItemService;
    private final CommonService commonService;
    private final TrialContractAuditMapper trialContractAuditMapper;

    /**
     * 保存试验协议合同
     * 此方法根据是否提交的状态更新协议合同的状态，并保存或更新与之相关的更新详情、取消详情及合同项目列表
     *
     * @param trialContract 试验协议合同及其相关详情对象
     * @param isSubmit      标识是否提交协议合同
     * @return 返回保存后的协议合同ID
     */
    @Override
    public String saveTrialContract(TrialContractAuditDetailVO trialContract, Boolean isSubmit) {
        // 编码唯一校验
        if (validateCode(trialContract)) {
            throw new RuntimeException("合同编号已存在");
        }
        // 根据是否提交，设置合同状态为“未提交”或“处理中”
        trialContract.setContractStatus(isSubmit ? ContractAuditStatusEnum.NO_SUBMIT.getCode() : ContractAuditStatusEnum.PROCESSING.getCode());
        // 保存或更新试验协议合同基本信息
        trialContractAuditService.createOrUpdateEntity(trialContract);

        // 如果存在更新详情，则关联合同ID并保存或更新更新详情
        TrialContractUpdateDetailVO trialContractUpdate = trialContract.getTrialContractUpdate();
        if (trialContractUpdate != null) {
            trialContractUpdate.setContractAuditId(trialContract.getId());
            trialContractUpdateService.createOrUpdateEntity(trialContractUpdate);
        }

        // 如果存在取消详情，则关联合同ID并保存或更新取消详情
        TrialContractCancelDetailVO trialContractCancel = trialContract.getTrialContractCancel();
        if (trialContractCancel != null) {
            trialContractCancel.setContractAuditId(trialContract.getId());
            trialContractCancelService.createOrUpdateEntity(trialContractCancel);
        }

        // 如果存在基础合同详情，则关联审计ID并保存或更新基础合同详情
        TrialContractDetailVO contract = trialContract.getTrialContract();
        if (contract != null) {
            contract.setAuditId(trialContract.getId());
            createOrUpdateEntity(contract);

            // 如果存在合同项目列表，则关联合同ID并批量保存或更新合同项目
            List<TrialContractItemListVO> trialContractItemList = contract.getTrialContractItemList();
            if (trialContractItemList != null) {
                trialContractItemList.forEach(item -> item.setTrialContractId(contract.getId()));
                trialContractItemService.createOrUpdateEntities(trialContractItemList);
            }
        }

        // 返回协议合同ID
        return trialContract.getId();
    }

    /**
     * 校验合同编号是否重复
     *
     * @param trialContract
     * @return true 有重复编码
     */
    private boolean validateCode(TrialContractAudit trialContract) {
        if (S.equals(trialContract.getAuditType(), ContractAuditTypeEnum.UPDATE.getCode()) || S.equals(trialContract.getAuditType(), ContractAuditTypeEnum.CANCEL.getCode())) {
            return false;
        }
        String id = trialContract.getId();
        LambdaQueryChainWrapper<TrialContractAudit> query = trialContractAuditService.lambdaQuery();
        if (id != null) {
            TrialContractAudit entity = trialContractAuditService.getEntity(id);
            query.ne(TrialContractAudit::getContractCode, entity.getContractCode());
        }
        query.eq(TrialContractAudit::getContractCode, trialContract.getContractCode());
        List<TrialContractAudit> entityList = trialContractAuditService.getEntityList(query);
        return entityList != null && entityList.size() > 0;
    }


    /**
     * 更新试验合同信息
     *
     * @param trialContract 试验合同审计详情对象，包含了试验合同更新的信息
     * @param isSubmit      是否提交合同，用于区分是保存为草稿还是正式提交
     * @return 返回当前合同的ID，如果提交则返回新合同ID，否则返回旧合同ID
     */
    @Override
    public String updateTrialContract(TrialContractAuditDetailVO trialContract, Boolean isSubmit) {
        if (isSubmit && !ContractAuditStatusEnum.canUpdate(trialContract.getContractStatus())) {
            throw new RuntimeException("当前合同未评审或评审中暂不允许变更");
        }
        // 获取试验合同更新详情
        TrialContractUpdateDetailVO trialContractUpdate = trialContract.getTrialContractUpdate();
        // 当前合同
        TrialContractAudit currentContract = null;
        // 暂存
        if (trialContractUpdate != null && trialContractUpdate.getId() == null) {
            // 获取当前合同信息，用于更新合同版本号
            currentContract = trialContractAuditService.getViewObject(trialContract.getId(), TrialContractAudit.class);
            // 更新合同版本号
            trialContract.setContractVersion(currentContract.getContractVersion() + 1);
            // 清除对象ID，准备保存新数据
            clearId(trialContract);
        } else {
            // 获取当前合同信息，基于合同编码和版本号
            currentContract = trialContractAuditService.getViewObject(trialContractAuditService.lambdaQuery()
                    .eq(TrialContractAudit::getContractCode, trialContract.getContractCode())
                    .eq(TrialContractAudit::getContractVersion, trialContract.getContractVersion() - 1), TrialContractAudit.class);
        }
        // 保存或更新试验合同信息
        trialContract.setAuditType("update");
        this.saveTrialContract(trialContract, isSubmit);
        if (isSubmit) {
            // 如果是提交合同，则更新当前合同状态为历史
            currentContract.setContractStatus(ContractAuditStatusEnum.HISTORY.getCode());
            // 保存或更新当前合同状态
            trialContractAuditService.createOrUpdateEntity(currentContract);
            // 返回新合同ID
            return trialContract.getId();
        } else {
            // 返回旧合同ID
            return currentContract.getId();
        }
    }

    @Override
    public String cancelTrialContract(TrialContractAuditDetailVO trialContract, Boolean isSubmit) {
        if (isSubmit && !ContractAuditStatusEnum.canCancel(trialContract.getContractStatus())) {
            throw new RuntimeException("当前合同未评审或评审中暂不允许取消");
        }
        trialContract.setAuditType("cancel");
        this.saveTrialContract(trialContract, isSubmit);
        return null;
    }

    @Override
    public String contractConfirmation(String id) {
        return null;
    }


    private void clearId(TrialContractAuditDetailVO trialContract) {

        TrialContractUpdateDetailVO trialContractUpdate = trialContract.getTrialContractUpdate();
        if (trialContractUpdate != null) {
            trialContractUpdate.setContractAuditId(null);
            trialContractUpdate.setId(null);
        }

        TrialContractCancelDetailVO trialContractCancel = trialContract.getTrialContractCancel();
        if (trialContractCancel != null) {
            trialContractCancel.setContractAuditId(null);
            trialContractCancel.setId(null);
        }

        TrialContractDetailVO contract = trialContract.getTrialContract();
        if (contract != null) {
            contract.setAuditId(null);
            contract.setId(null);
            List<TrialContractItemListVO> trialContractItemList = contract.getTrialContractItemList();
            if (trialContractItemList != null) {
                trialContractItemList.forEach(item -> {
                    item.setTrialContractId(null);
                    item.setId(null);
                });
            }
        }
    }


    /**
     * 根据合同ID获取更新保存的合同详情
     * 此方法首先尝试获取已更新但未提交的合同详情，如果不存在，则转换并返回原始合同实体的详情
     *
     * @param id 合同ID
     * @return 更新保存的合同详情对象
     */
    @Override
    public TrialContractAuditDetailVO getUpdateSavedContract(String id) {
        // 根据ID获取合同审计实体
        TrialContractAudit entity = trialContractAuditService.getEntity(id);

        // 尝试获取更新且未提交的合同详情
        TrialContractAuditDetailVO contractDetail = trialContractAuditService.getViewObject(
                trialContractAuditService.lambdaQuery()
                        .eq(TrialContractAudit::getContractCode, entity.getContractCode())
                        .eq(TrialContractAudit::getContractVersion, entity.getContractVersion() + 1)
                        .eq(TrialContractAudit::getAuditType, "update")
                        .eq(TrialContractAudit::getContractStatus, ContractAuditStatusEnum.NO_SUBMIT.getCode()),
                TrialContractAuditDetailVO.class
        );

        // 如果存在未提交的更新合同详情，则直接返回
        if (contractDetail != null) {
            return contractDetail;
        } else {
            // 否则，将原始合同实体转换为合同详情对象，并初始化更新详情对象
            TrialContractAuditDetailVO trialContractAuditDetailVO = Binder.convertAndBindRelations(entity, TrialContractAuditDetailVO.class);
            trialContractAuditDetailVO.setTrialContractUpdate(new TrialContractUpdateDetailVO());
            return trialContractAuditDetailVO;
        }
    }

    @Override
    public List<TrialContractAuditListVO> getContractHistory(TrialContractAuditDTO queryDto, Pagination pagination) {
        QueryWrapper<TrialContractAudit> queryWrapper = QueryBuilder.toQueryWrapper(queryDto);
        queryWrapper.lambda().eq(TrialContractAudit::getContractStatus, ContractAuditStatusEnum.HISTORY.getCode());
        return trialContractAuditService.getViewObjectList(queryWrapper, pagination, TrialContractAuditListVO.class);
    }

    @Override
    public List<TrialContractAuditListVO> selectContractList(TrialContractAuditDTO queryDto, Pagination pagination,String type, String permission) {
        // 根据查询条件和分页信息生成查询包装器
        QueryWrapper<TrialContractAudit> queryWrapper = QueryBuilder.toQueryWrapper(queryDto, pagination);
        queryWrapper.lambda().orderBy(true, false, TrialContractAudit::getId);
        // 根据类型设置查询条件
        if (S.equals(type, "draft")) { // 草稿类型
            queryWrapper.lambda()
                    .eq(TrialContractAudit::getContractStatus, ContractAuditStatusEnum.NO_SUBMIT.getCode())
                    .eq(TrialContractAudit::getCreateBy, IamSecurityUtils.getCurrentUserId())
                    .eq(TrialContractAudit::getAuditType, "create");
        } else if (S.equals(type, "formal")) { // 正式类型
            queryWrapper.lambda()
                    .ne(TrialContractAudit::getContractStatus, ContractAuditStatusEnum.NO_SUBMIT.getCode())
                    .ne(TrialContractAudit::getContractStatus, ContractAuditStatusEnum.HISTORY.getCode())
                    .ne(TrialContractAudit::getContractStatus, ContractAuditStatusEnum.ARCHIVED.getCode());
        }

        if (S.equals(queryDto.getAuditType(), ContractAuditTypeEnum.UPDATE.getCode()) || S.equals(queryDto.getAuditType(), ContractAuditTypeEnum.CANCEL.getCode())) {
            queryWrapper.lambda().ne(TrialContractAudit::getContractStatus, ContractAuditStatusEnum.HISTORY.getCode())
                    .ne(TrialContractAudit::getContractStatus, ContractAuditStatusEnum.ARCHIVED.getCode())
                    .ne(TrialContractAudit::getContractStatus, ContractAuditStatusEnum.NO_SUBMIT.getCode());
        }

        // 根据权限设置查询条件
        if (S.equals(permission, "self")) { // 本人权限
            queryWrapper.lambda().eq(TrialContractAudit::getCreateBy, IamSecurityUtils.getCurrentUserId());
        } else if (S.equals(permission, "sub")) { // 下属权限
            List<String> subEmp = commonService.findSubEmp(IamSecurityUtils.getCurrentUser());
            if (subEmp.isEmpty()) {
                return new ArrayList<>();
            }
            queryWrapper.lambda().in(TrialContractAudit::getCreateBy, subEmp);
        } else if (S.equals(permission, "star")) { // 收藏权限
            List<String> starContractIds = trialContractAuditMapper.getStarContractIds(IamSecurityUtils.getCurrentUserId());
            if (starContractIds.isEmpty()) {
                return new ArrayList<>();
            }
            queryWrapper.lambda().in(TrialContractAudit::getId, starContractIds);
        } else if (S.equals(permission, "all")) { // 本人及下属权限
            List<String> subEmp = commonService.findSubEmp(IamSecurityUtils.getCurrentUser());
            subEmp.add(IamSecurityUtils.getCurrentUserId());
            queryWrapper.lambda().in(TrialContractAudit::getCreateBy, subEmp);
        }
        // 查询审批列表视图对象列表
        List<TrialContractAuditListVO> viewObjectList = getViewObjectList(queryWrapper, pagination, TrialContractAuditListVO.class);
        // 遍历视图对象列表，判断当前用户是否收藏了该合同，如果是，则设置收藏标志为true
        for (TrialContractAuditListVO trialContractAuditListVO : viewObjectList) {
            List<String> starUids = trialContractAuditListVO.getStarUids();
            if (starUids != null && starUids.contains(IamSecurityUtils.getCurrentUserId())) {
                trialContractAuditListVO.setIsStar(true);
            }
        }
        return viewObjectList;
    }


    @Override
    public JsonResult<String> deleteContract(List<String> ids) {
        List<TrialContractAudit> entities = trialContractAuditService.getEntityListByIds(ids);
        for (TrialContractAudit entity : entities) {
            if (!ContractAuditStatusEnum.canDelete(entity.getContractStatus())){
                return JsonResult.FAIL_VALIDATION(entity.getContractCode()+"已提交不允许删除");
            }
        }
        trialContractAuditService.deleteEntities(entities);

        return JsonResult.OK();
    }
}